package org.sradon.mySpring.model;

import org.sradon.mySpring.anno.ioc.Component;
import org.sradon.mySpring.anno.ioc.Repository;
import org.sradon.mySpring.anno.ioc.Service;
import org.sradon.mySpring.context.BeanDefine;
import org.sradon.mySpring.utils.AnnoUtils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

public class IocExecutor {
    /**
     * IOC注解数组
     * 用于在加载类之后匹配IOC注解, 因为递归匹配有点问题, 换了比较粗糙的暴力方案
     */
    public static final Class<? extends Annotation>[] IOC_ANNOS
            = new Class[]{Component.class, Service.class, Repository.class};


    /**
     * 递归处理文件系统目录, 查找出所有字节码文件
     * @param dir - 目标目录
     * @param packageName - 包名
     * @param loader - 类加载器
     */
    public static String BeanLoader(File dir, String packageName, ClassLoader loader, Map<String, Object> beans, List<BeanDefine> defines) {
        if (!dir.exists()) return null;

        // 遍历目录中的所有文件/子目录
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (file.isDirectory()) {
                // 递归处理子目录
                BeanLoader(file, packageName + "." + file.getName(), loader, beans, defines);
            } else if (file.getName().endsWith(".class")) {
                // 仅处理.class文件
                String className = packageName + "." + file.getName().replace(".class", "");
                // System.out.println("[INFO @ioc] 正在处理: " + className);

                // 处理字节码文件
                try {
                    // 1. 加载字节码文件, 获取字节码对象
                    Class<?> clazz = loader.loadClass(className);
                    // 2. 获取字节码对象注解, 判断有没有被IOC注解修饰
                    Class<? extends Annotation> iocAnno = AnnoUtils.isComponent(clazz);
                    // 3. 创建对象并放入容器
                    if (iocAnno != null) {
                        // 3.1 创建对象
                        Object bean = clazz.newInstance();

                        // 3.2 执行初始化方法(如果有的话)
                        AnnoUtils.useInit(clazz, bean);

                        // 3.3 定义Bean的Key
                        String beanKeyInContext = null;

                        /// 3.3.1 如果IOC注解中有value属性, 则用value属性作key
                        // 定义IOC注解的value值
                        String annoValue = null;
                        // 获取IOC注解的value值
                        Annotation annotation = clazz.getDeclaredAnnotation(iocAnno);

                        // 获取IOC注解中的value值
                        try {
                            // 通过反射获取value()方法
                            Method valueMethod = annotation.annotationType().getMethod("value");
                            // 调用value()方法获取值
                            annoValue = (String) valueMethod.invoke(annotation);
                        } catch (Exception e) {
                            // 忽略没有 value 方法的注解
                        }

                        /// 3.4 分情况设置Bean的Key
                        // 3.4.a. 如果有value属性, 则用value属性作为key
                        if (annoValue != null && !annoValue.isEmpty())
                            beanKeyInContext = annoValue;
                        /* 引入定义列表方案后已弃用. 如果没有value属性, 则用接口名作为key
//                        else if (clazz.getInterfaces().length > 0)
//                            beanKeyInContext = clazz.getInterfaces()[0].getName();
                         */
                        // 3.4.b. 默认用全限定类名作为 key
                        else
                            beanKeyInContext = clazz.getName();
                        // 3.5 放入容器和定义列表
                        putBean(beans, defines, beanKeyInContext, bean, clazz);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("[ERR @ioc] 处理对象" + className + "时失败...");
                }
            } else {
                // 忽略非class文件（如.txt/.jpg等）
                // System.out.println("跳过非class文件: " + file.getName());
            }
        }
        return null;
    }

    /**
     * 放入容器和索引
     * @param beans 容器
     * @param defines 索引
     * @param beanKeyInContext Bean在容器中的Key
     * @param bean 要放的, 创建好了的对象
     * @param clazz 该对象的字节码对象
     */
    public static void putBean(Map<String, Object> beans,
                                List<BeanDefine> defines,
                                String beanKeyInContext,
                                Object bean,
                                Class<?> clazz) {
        // 查重
        if (beans.get(beanKeyInContext) != null)
            throw new RuntimeException("[ERR @ioc] 容器中已存在对象: " + beanKeyInContext);

        // 放入容器和索引
        beans.put(beanKeyInContext, bean);
        defines.add(
                new BeanDefine(beanKeyInContext, clazz, clazz.getInterfaces(), clazz.getSuperclass())
        );
        System.out.println("[INFO @ioc] ADD Bean<" + clazz + "> : KEY '" + beanKeyInContext + "'");
    }

//    /**
//     * 加载类并检查注解
//     * @param loader - 类加载器
//     * @param className - 完整类名
//     */
//    private static void findAnnoInClass(ClassLoader loader, String className) {
//        try {
//            // 加载类（仅加载.class文件对应的类）
//            Class<?> clazz = loader.loadClass(className);
//
//            // 检查类是否有指定注解（示例）
//            if (findAnnoInAnno(clazz, Component.class)) {
//
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }



}
